package statistics;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.*;
import jxl.Workbook;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import org.bson.Document;
import utils.FileLoadUtil;
import utils.MongoMultiCursor;

import java.io.FileOutputStream;
import java.util.*;
import java.util.stream.Collectors;

public class MultiQualityReport {

    public static void main(String[] args) {
//        Properties properties = FileLoadUtil.loadProperties("E:\\adc\\branches\\v2.2.1\\conf\\qualityreport.properties");
        Properties properties = FileLoadUtil.loadProperties(args[0]);
        String path = properties.getProperty("path");
        String host = properties.getProperty("host");
        String port = properties.getProperty("port");
        String user = properties.getProperty("user");
        String psw = properties.getProperty("psw");
        String db = properties.getProperty("db");
        String integrity = properties.getProperty("Integrity");
        MultiQualityReport statistics = new MultiQualityReport();
        statistics.statisticsNoSrc(path, host, port, user, psw, db, integrity);
    }

    private void statisticsNoSrc(String path, String host, String port, String user, String psw, String db, String integrity) {
        long start = System.currentTimeMillis();
        // 创建Mongo连接
        List<ServerAddress> serverAddressList = new ArrayList<>();
        ServerAddress address = new ServerAddress(host, Integer.parseInt(port));
        serverAddressList.add(address);
        MongoCredential mongoCredential = MongoCredential.createCredential(user, "admin", psw.toCharArray());
        MongoClient client = new MongoClient(serverAddressList, mongoCredential, getOptions());


        // 读指标配置拿到指标  targetMap< HS100001, Map<main_doc、expect_doc> >
        JSONArray jsonArr = JSON.parseArray(integrity);
        int size = jsonArr.size();

        HashMap<String, Map> targetMap = new HashMap<>();
        HashSet<String> tables = new HashSet<>();

        for (Object o : jsonArr) {
            JSONObject JsonObj = (JSONObject) o;
            String id = (String) JsonObj.keySet().toArray()[0];
            JSONObject jsonObject = JsonObj.getJSONObject(id);
            String main_doc = jsonObject.getString("main_doc");
            String expect_doc = jsonObject.getString("expect_doc");
            String explain = jsonObject.getString("explain");
            JSONArray main_confine = jsonObject.getJSONArray("main_confine");
            HashMap<String, Object> main2expectMap = new HashMap<>();
            main2expectMap.put("main_doc", main_doc);
            main2expectMap.put("expect_doc", expect_doc);
            main2expectMap.put("explain", explain);
            if (main_confine != null) {
                main2expectMap.put("main_confine", main_confine);
            }
            targetMap.put(id, main2expectMap);

            tables.add(main_doc);
            List<String> collect = Arrays.stream(expect_doc.split(",")).collect(Collectors.toList());
            tables.addAll(collect);
        }

        ArrayList<String> tbs = new ArrayList<>(tables);
        MongoMultiCursor multiCursor = new MongoMultiCursor(client, tbs, db);

        // 临时用
        HashMap<String, HashMap<String, Object>> result = new HashMap<>();
        HashMap<String, ArrayList<String>> failed = new HashMap<>();

        long cnt = 0;
        // 按就诊次遍历，每次拿到一个就诊次的全部数据
        while (multiCursor.hasNext()) {
            System.out.println("开始处理第" + cnt++ + "条数据");
            if (cnt > 100) {break;}
            List<Document> next = multiCursor.next();
            String id = next.get(0).getString("_id");
            List<String> docList = new ArrayList<>();
            // 属性字段,用于此诊次判断是否符合特殊条件
            HashMap<String, String> attribute = new HashMap<>();
            for (Document document : next) {
                Set<String> keySet = document.keySet();
                keySet.retainAll(tbs);
                docList.addAll(keySet);
                if (keySet.contains("binganshouye")) {
                    Document binganshouye = new Document();
                    if (document.get("binganshouye") instanceof Document) {
                        binganshouye = (Document) document.get("binganshouye");
                    } else {
                        continue;
                    }
                    Document pat_visit = (Document) binganshouye.get("pat_visit");
                    if (pat_visit != null) {
                        String in_hospital_days = pat_visit.getString("in_hospital_days");
                        String discharge_class_name = pat_visit.getString("discharge_class_name");
                        attribute.put("in_hospital_days", in_hospital_days);
                        attribute.put("discharge_class_name", discharge_class_name);
                    }
                }
            }

            Set<String> targetIds = targetMap.keySet();
            for (String targetId : targetIds) {
                Map target = targetMap.get(targetId);
                String main_doc = target.get("main_doc").toString();
                String expect_doc = target.get("expect_doc").toString();
                String explain = target.get("explain").toString();
                Object main_confine_str = target.get("main_confine");
                JSONArray main_confine = new JSONArray();
                boolean hasConfine = false;
                if (main_confine_str != null && !main_confine_str.equals("")) {
                    main_confine = JSON.parseArray(main_confine_str.toString());
                    hasConfine = true;
                }
                // 符合约束条件为 ture
                boolean conform = false;
                // 判断是否此就诊次是否符合约束条件
                if (hasConfine) {
                    for (Object o : main_confine) {
                        JSONObject confines = (JSONObject) o;
                        JSONArray and = confines.getJSONArray("and");
                        if (and != null) {
                            // 与操作,为确保and所有的条件都符合
                            boolean flag = true;
                            for (Object o1 : and) {
                                JSONObject item = (JSONObject) o1;
                                String field = item.getString("field");
                                String mode = item.getString("mode");
                                String value = item.getString("value");
                                if (field.equals("住院天数")) {
                                    if (mode.equals("=")) {
                                        flag = value.equals(attribute.getOrDefault("in_hospital_days", "")) && flag;
                                    }
                                    if (mode.equals("!")) {
                                        flag = !value.equals(attribute.getOrDefault("in_hospital_days", "")) && flag;
                                    }
                                    if (mode.equals(">")) {
                                        String in_hospital_days = attribute.get("in_hospital_days");
                                        if (in_hospital_days != null) {
                                            int i = Integer.parseInt(in_hospital_days);
                                            int i1 = Integer.parseInt(value);
                                            flag = (i > i1) && flag;
                                        } else {
                                            flag = false;
                                        }
                                    }
                                    if (mode.equals("<")) {
                                        String in_hospital_days = attribute.get("in_hospital_days");
                                        if (in_hospital_days != null) {
                                            int i = Integer.parseInt(in_hospital_days);
                                            int i1 = Integer.parseInt(value);
                                            flag = (i < i1) && flag;
                                        } else {
                                            flag = false;
                                        }
                                    }
                                } else if (field.equals("出院方式")) {
                                    if (mode.equals("=")) {
                                        flag = value.equals(attribute.getOrDefault("discharge_class_name", "")) && flag;
                                    }
                                    if (mode.equals("!")) {
                                        flag = !value.equals(attribute.getOrDefault("discharge_class_name", "")) && flag;
                                    }
                                }
                                conform = flag;
                            }
                        }
                    }
                }

                List<String> expect_docs = Arrays.stream(expect_doc.split(",")).collect(Collectors.toList());
                expect_docs.retainAll(docList);

                if ((docList.contains(main_doc) && expect_docs.size() != 0 && !hasConfine)
                        || (docList.contains(main_doc) && expect_docs.size() != 0 && hasConfine && conform)) {
                    // 发送消息 1
                    HashMap<String, Object> itemResult = result.getOrDefault(targetId, new HashMap<>());
                    int success = (int) itemResult.getOrDefault("success", 0);
                    if (!itemResult.containsKey("explain")) {
                        itemResult.put("explain", explain);
                    }
                    itemResult.put("success", success + 1);
                    result.put(targetId, itemResult);
                } else if ((docList.contains(main_doc) && expect_docs.size() == 0 && !hasConfine)
                        || (docList.contains(main_doc) && expect_docs.size() == 0 && hasConfine && conform)) {
                    // 发送消息 0
                    HashMap<String, Object> itemResult = result.getOrDefault(targetId, new HashMap<>());
                    int fail = (int) itemResult.getOrDefault("fail", 0);
                    if (!itemResult.containsKey("explain")) {
                        itemResult.put("explain", explain);
                    }
                    itemResult.put("fail", fail + 1);
                    result.put(targetId, itemResult);
                    ArrayList<String> tmp_failed = failed.getOrDefault(targetId, new ArrayList<>());
                    tmp_failed.add(id);
                    failed.put(targetId, tmp_failed);
                }
            }

        }
        System.out.println("result = " + result);
        String head1[] = {"指标代码", "指标说明", "符合条数", "不符合条数", "符合率"};
        String results1[][] = new String[size][5];
        List<String> keys = new ArrayList<>(result.keySet());
        keys.sort(Comparator.naturalOrder());
        for (int i = 0; i < keys.size(); i++) {
            String id = keys.get(i);
            HashMap<String, Object> map = result.get(id);
            int success = (int) map.getOrDefault("success", 0);
            int fail = (int) map.getOrDefault("fail", 0);
            String explain = (String) map.getOrDefault("explain", "");
            results1[i][0] = id;
            results1[i][1] = explain;
            results1[i][2] = success + "";
            results1[i][3] = fail + "";
            results1[i][4] = (Math.round(success * 10000 / (success + fail)) / 100.0) + "%";
        }

        String head2[] = new String[size];
        String results2[][] = new String[100][size];
        List<String> keys2 = new ArrayList<>(failed.keySet());
        for (int i = 0; i < keys2.size(); i++) {
            String id = keys2.get(i);
            ArrayList<String> list = failed.get(id);
            head2[i] = id;
            for (int i1 = 0; i1 < list.size() && i1 < 100; i1++) {
                results2[i1][i] = list.get(i1);
            }
        }

        try {
            createExcel(path, head1, results1, head2, results2);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    private static MongoClientOptions getOptions() {
        MongoClientOptions.Builder options = new MongoClientOptions.Builder();
        //最小连接数
        options.minConnectionsPerHost(5);
        options.maxConnectionIdleTime(1800000);
        options.maxConnectionLifeTime(1800000);
        options.connectionsPerHost(Integer.parseInt("300"));// 连接池设置为300个连接,默认为100
        options.connectTimeout(Integer.parseInt("3000"));// 连接超时，推荐>3000毫秒
        options.maxWaitTime(Integer.parseInt("5000")); //
        options.socketTimeout(Integer.parseInt("0"));//套接字超时时间，0无限制
        // 线程队列数，如果连接线程排满了队列就会抛出“Out of semaphores to get db”错误。
        options.threadsAllowedToBlockForConnectionMultiplier(Integer.parseInt("5000"));
        /**majority 会出现bug：
         * code=64, message='waiting for replication timed out at shard3', details={ }
         * **/
//            WriteConcern majority = new WriteConcern("majority", 2000, false, true);
        WriteConcern majority = new WriteConcern(1, 2000, false, true);
//            WriteConcern majority = WriteConcern.W1;
        options.writeConcern(majority);
        options.readPreference(ReadPreference.primaryPreferred());
        options.build();
        return options.build();
    }

    /**
     * url:要导出创建的excel文档的路径位置及名字
     * eg:"文档路径...\\文档名.xls"
     * <p>
     * head[]:表格首行标题
     * result[][]:以二维数组形式存放的表格文档内容
     */
    public static void createExcel(String url, String head1[], String result1[][], String head2[], String result2[][]) throws Exception {

        FileOutputStream os = new FileOutputStream(url);

        //创建工作薄
        WritableWorkbook workbook = Workbook.createWorkbook(os);
        //创建新的一页
        WritableSheet sheet = workbook.createSheet("指标", 0);
        //创建新的二页
        WritableSheet sheet2 = workbook.createSheet("不符合ID", 1);
        //创建要显示的具体内容

        //首行标题内容
        for (int i = 0; i < head1.length; i++) {
            jxl.write.Label l1 = new jxl.write.Label(i, 0, head1[i]);
            sheet.addCell(l1);
        }
        //内容
        //排序
//        Arrays.sort(result1,Comparator.comparingDouble(arr -> Double.parseDouble(arr[arr.length - 1].split("%")[0])));
        for (int i = 0; i < result1.length; i++) {

            for (int j = 0; j < result1[0].length; j++) {
                jxl.write.Label l1 = new jxl.write.Label(j, i + 1, result1[i][j]);
                sheet.addCell(l1);
            }
        }

        //首行标题内容
        for (int i = 0; i < head2.length; i++) {
            jxl.write.Label l1 = new jxl.write.Label(i, 0, head2[i]);
            sheet2.addCell(l1);
        }
        //内容
        for (int i = 0; i < result2.length; i++) {

            for (int j = 0; j < result2[0].length; j++) {
                jxl.write.Label l1 = new jxl.write.Label(j, i + 1, result2[i][j]);
                sheet2.addCell(l1);
            }
        }
        workbook.write();
        workbook.close();
        os.close();
    }

}
